/*
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#ifdef _MSC_VER
#define BOOST_PYTHON_STATIC_LIB
#endif

#include "boost/python.hpp"
using namespace boost::python;

#include "NTree.h"
#include "Prerequisites.h"
#include "Vector3i.h"
#include "OgreMath/OgreVector3.h"
#include "OgreMath/OgreQuaternion.h"
#include "Constructors.h"
#include <memory>

typedef NTree<Vector3i, MaterialID> SolidTree;

class PyVolume
{
protected:
	std::shared_ptr<SolidTree> m_Tree;

	Ogre::Vector3 m_Position, m_Scale;
	Ogre::Quaternion m_Orientation;

	MaterialID m_MaterialID;

	bool m_NeedsUpdate;
	bool m_WasTransformed;

	//virtual void performExtrude(PyVolume &out, PyVolume &toExtrude);
	virtual void performExtrudeStep(SolidTree &outTree, const Vector3i &offset);
	virtual void forceEval();

public:

	template<class BaseCon>
	class TransformConstructor
	{
	private:
		int size;
		std::shared_ptr<Transform3DCon<BaseCon>> transformConstructor;
		std::shared_ptr<MoveCon<Vector3i, BaseCon>> moveConstructor;
		bool needsFullTransform;

	public:
		TransformConstructor() : transformConstructor(nullptr), moveConstructor(nullptr), needsFullTransform(false) {}
		~TransformConstructor() {}
		void setMove(const Ogre::Vector3 &position, const BaseCon& baseConstructor)
		{
			moveConstructor = std::make_shared<MoveCon<Vector3i, BaseCon>>(position, baseConstructor);
			size = moveConstructor->getSize();
			needsFullTransform = false;
		}
		void setTransform(const Ogre::Vector3 &position, const Ogre::Vector3 &scale, const Ogre::Quaternion &orientation, const BaseCon& baseConstructor)
		{
			transformConstructor = std::make_shared<Transform3DCon<BaseCon>>(position, scale, orientation, baseConstructor);
			size = transformConstructor->getSize();
			needsFullTransform = true;
		}

		const int getSize() const {return size;}

		const VoxelType fn(const Box<Vector3i>& box) const
		{
			if (!needsFullTransform)
				return moveConstructor->fn(box);
			else
				return transformConstructor->fn(box);
		}
	};

protected:
	TransformConstructor<CopyCon<Vector3i, MaterialID>> copyConMover;
	void updateCopyConMover();

public:

	PyVolume() :
		m_Tree(std::make_shared<SolidTree>()), m_Position(Ogre::Vector3(0,0,0)), m_Scale(Ogre::Vector3(1,1,1)), m_Orientation(Ogre::Quaternion()), m_MaterialID(DEFAULT_MATERIAL), m_NeedsUpdate(false), m_WasTransformed(false) {}
	PyVolume(std::shared_ptr<SolidTree> _tree) : m_Tree(_tree), m_Position(Ogre::Vector3(0,0,0)), m_Scale(Ogre::Vector3(1,1,1)), m_Orientation(Ogre::Quaternion::IDENTITY), m_MaterialID(DEFAULT_MATERIAL), m_NeedsUpdate(false), m_WasTransformed(false) {}
	virtual ~PyVolume() {}

	void exportAsObj(std::string fileName, float voxelsPerUnit, int numSmoothIterations);
	void exportAsStl(std::string fileName, float voxelsPerUnit, int numSmoothIterations);
	void exportAsPly(std::string fileName, float voxelsPerUnit, int numSmoothIterations);

	virtual void setPosition(const Ogre::Vector3 &position);
	virtual void setScale(const Ogre::Vector3 &scale);
	virtual void setRotation(float xRotDeg, float yRotDeg, float zRotDeg);

	virtual void setMaterial(const MaterialID& material);

	std::shared_ptr<SolidTree> getTree() { return m_Tree; }

	PyVolume unionOp(PyVolume &other);
	PyVolume differenceOp(PyVolume &other);
	PyVolume intersectionOp(PyVolume &other);

	// PyVolume extrude(PyVolume &other);
	// PyVolume extrudeDynamic(const boost::python::object &extruder);

	PyVolume thicken(float radius);

	PyVolume roundConcaveOp(float radius);
	PyVolume roundConvexOp(float radius);
	PyVolume offsetOp(float radius);
	PyVolume insetOp(float radius);
	PyVolume surfels();

	PyVolume copy();
	void clear();

	boost::python::tuple getExtents();
	int countNodes();
	int countVoxels();
	Ogre::Vector3 centerOfMass();
	void checkTree();

	/*class SimpleGeneralExtruder
	{
	private:
		TransformConstructor<CopyCon<Vector3i, MaterialID>> con;

	public:
		SimpleGeneralExtruder(const TransformConstructor<CopyCon<Vector3i, MaterialID>> &_con) : con(_con) {}
		std::pair<TransformConstructor<CopyCon<Vector3i, MaterialID>>, MaterialID> fn(float fPos) const
		{
			return std::make_pair(con, DEFAULT_MATERIAL);
		}
	};*/

	class PythonExtruder
	{
	protected:
		boost::python::object extruder;
	public:
		PythonExtruder(const boost::python::object &_extruder) : extruder(_extruder) {}
		virtual ~PythonExtruder() {}
		void extrude (SolidTree &out, const Vector3i position, float fPos) const;
	};

};
